package com.example.erp.log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Aspect
@Order(1)
public class LogAspect {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    public static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();

    private final ObjectMapper mapper;

    private static final int LOG_MAX_WIDTH = 1000000;

    @Autowired
    public LogAspect(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Pointcut("@annotation(com.example.erp.log.LogPrint)")
    private void cut() {
    }

    /**
     * 打印方法返回参数和调用方法耗时，单位毫秒
     */
    @Around("cut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        long startTime = System.currentTimeMillis();
        final Object proceed = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        //打印调用方法的返回参数和调用本方法的总耗时
        if ((proceed instanceof ResponseEntity<?> && ((ResponseEntity<?>) proceed).getBody() instanceof byte[]) || proceed instanceof byte[]) {
            logger.info("trsId={}, method={}, cost time={}ms, response params={} ", THREAD_LOCAL.get(), methodName, endTime - startTime, "");
        } else {
            logger.info("trsId={}, method={}, cost time={}ms, response params={}", THREAD_LOCAL.get(), methodName, endTime - startTime, StringUtils.abbreviate(mapper.writeValueAsString(proceed), LOG_MAX_WIDTH));
        }
        return proceed;
    }

    /**
     * 打印方法入参
     */
    @Before("cut()")
    public void before(JoinPoint joinPoint) throws JsonProcessingException {
        StringBuilder param = new StringBuilder();
        for (Object object : joinPoint.getArgs()) {
            if (object instanceof MultipartFile || object instanceof HttpServletRequest || object instanceof HttpServletResponse) {
                continue;
            }
            param.append(mapper.writeValueAsString(object)).append(",");
        }
        String methodName = joinPoint.getSignature().getName();
        THREAD_LOCAL.set(RandomStringUtils.randomAlphanumeric(6));
        //打印方法入参
        logger.info("trsId={}, method={}, request params={}", THREAD_LOCAL.get(), methodName, param.toString());
    }
}
